/**
 * Created by chang on 2017/2/14.
 */
const moment = require('moment');
const uuidV1 = require('uuid/v1');
const uuidV4 = require('uuid/v4');
const md5 = require('md5');

const helper = module.exports;

helper.uuid = (dna) => {
    dna = dna || '';
    return uuidV4().substr(9, 27) + '-' + uuidV1().substr(24, 12) + '-' + md5(dna).substr(0, 5)
};

helper.uuidV1 = () => {
    return uuidV1();
};

helper.uuidV4 = () => {
    return uuidV4();
};

helper.now = (fmt) => {
    fmt = fmt || "YYYY-MM-DD HH:mm:ss";
    return moment().format(fmt);
};

helper.md5 = (msg) => {
    return md5(msg);
};

helper.rdmFlowNo = () => {
    return this.now('YYYYMMDDHHmmss') + Math.floor(Math.random() * 100000);
};

helper.parse = (msg) => {
    return JSON.parse(JSON.stringify(msg));
};

helper.toTimeString = (time, fmt) => {
    if (!time) return '';

    fmt = fmt || "YYYY-MM-DD HH:mm:ss";
    return moment(time).format(fmt)
};

helper.getQueryString = (name, search) => {
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    let regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
    let results = regex.exec(search);
    return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};

helper.formatMoney = (number, places, symbol, thousand, decimal) => {
    number = number || 0;
    places = !isNaN(places = Math.abs(places)) ? places : 0;
    symbol = symbol !== undefined ? symbol : "¥";
    thousand = thousand || ",";
    decimal = decimal || ".";
    let negative = number < 0 ? "-" : "",
        i = parseInt(number = Math.abs(+number || 0).toFixed(places), 10) + "";
    let j = 0;
    j = (j = i.length) > 3 ? j % 3 : 0;
    return symbol + negative + (j ? i.substr(0, j) + thousand : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousand) + (places ? decimal + Math.abs(number - i).toFixed(places).slice(2) : "");
};


helper.convertBase = (value, from_base, to_base) => {
    value = value.toString();
    let range = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/'.split('');
    let from_range = range.slice(0, from_base);
    let to_range = range.slice(0, to_base);

    let dec_value = value.split('').reverse().reduce(function (carry, digit, index) {
        if (from_range.indexOf(digit) === -1) throw new Error('Invalid digit `' + digit + '` for base ' + from_base + '.');
        return carry += from_range.indexOf(digit) * (Math.pow(from_base, index));
    }, 0);

    let new_value = '';
    while (dec_value > 0) {
        new_value = to_range[dec_value % to_base] + new_value;
        dec_value = (dec_value - (dec_value % to_base)) / to_base;
    }
    return new_value || '0';
};

helper.fileType = (filename) => {
    let extname = path.extname(filename).toLowerCase();

    if (!!~'.mp3'.indexOf(extname)) {
        return {type: 'audio', name: '音频', flag: 0, path: '/我的音频'};
    } else if (!!~'.jpg,.jpeg,.png,.gif'.indexOf(extname)) {
        return {type: 'image', name: '图片', flag: 1, path: '/我的图片'};
    } else if (!!~'.avi,.wmv,.mpeg,.mp4,.mov,.mkv,.flv,.f4v,.m4v,.rmvb,.rm,.3gp,.dat,.ts,.mts,.vob'.indexOf(extname)) {
        return {type: 'video', name: '视频', flag: 2, path: '/我的视频'};
    } else if (!!~'.svg'.indexOf(extname)) {
        return {type: 'svg', name: '矢量图', flag: 3, path: '/矢量图'};
    } else {
        return {type: 'other', name: '其他', flag: 4, path: '/其他'};
    }
};

helper.replaceExt = (file, ext) => {
    let xp = file.lastIndexOf('.');
    return file.substring(0, xp + 1) + ext;
};

helper.isCellPhone = (n) => {
    return /^1\d{10}$/.test(n);
};

helper.isEmail = (n) => {
    return /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(n);
};

helper.isPrice = (n) => {
    return /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/.test(n);
};

helper.rangeRandom = (min, max, count) => {
    let range = [];
    _.times(count, () => {
        let flag = true;
        while (flag) {
            let _rdm = _.random(min, max);
            if (max - min < count) {
                range.push(_.random(min, max));
                flag = false;
            }

            if (!~range.findIndex(f => f === _rdm)) {
                range.push(_rdm);
                flag = false;
            }
        }


    });
    return range;
};


// 获取访问IP
helper.getClientIP = function (req) {
    let ipAddress = '';
    let headers = req.headers;
    let forwardedIpsStr = headers['x-real-ip'] || headers['x-forwarded-for'];
    forwardedIpsStr ? ipAddress = forwardedIpsStr : ipAddress = null;
    if (!ipAddress) {
        ipAddress = req.connection.remoteAddress;
    }
    return ipAddress;
};

/**
 * 获取一组数据
 * @param modal     数据库模型
 * @param where     条件
 * @param page      开始位置
 * @param limit     分页大小
 * @param order     排序方式
 * @param include   join操作
 *         [
 *             {
 *             foreignKey:主表外键，
 *             targetKey:'链接表主键'，
 *             modal: 链接表模型，
 *             where:查询条件，
 *             limit: 限制数量
 *             order：排序，
 *             attributes:属性，
 *             as:'别名'，
 *             relation:'one' or 'many' or 'has' ,
 *             include:[子子查询]}
 *         ]
 * @param attributes
 * @returns {Promise<*>}
 */
helper.getList = async (modal, where = {}, page = 1, limit = 10, order = [], include = [], attributes = {}) => {
    let offset = (page - 1) * limit;

    let ret = await modal.findAndCountAll(
        {
            attributes: attributes,
            offset: offset,
            limit: limit,
            where: where,
            order: order,
        }
    );

    ret = helper.parse(ret);

    // 模拟join 操作
    if (!_.isEmpty(include)) {
        if (ret.rows && _.isArray(ret.rows) && !_.isEmpty(ret.rows)) {
            // 可能有多个join 字段
            for (let ii = 0; ii < include.length; ii++) {
                let currentInclude = include[ii];

                // 提取外键并去重
                let foreignKeys = ret.rows.map(m => m[currentInclude.foreignKey]).filter((f, i, a) => a.findIndex(fa => fa === f) === i);

                let where = currentInclude.where || {};
                let order = currentInclude.order || [];
                let attributes = currentInclude.attributes || {};

                where[currentInclude.targetKey] = {$in: foreignKeys};


                let findArgs = {
                    where: where,
                    order: order,
                    attributes: attributes
                };

                if(currentInclude.limit) {
                    findArgs.limit = currentInclude.limit;
                }

                let targetObjects = await currentInclude.modal.findAll(findArgs);

                targetObjects = helper.parse(targetObjects);

                // 内部查询
                if (!_.isEmpty(currentInclude.include)) {
                    for (let cii = 0; cii < currentInclude.include.length; cii++) {
                        let subInclude = currentInclude.include[cii];

                        let fk = targetObjects.map(m => m[subInclude.foreignKey]).filter((f, i, a) => a.findIndex(fa => fa === f) === i);
                        let wh = subInclude.where || {};
                        let odr = subInclude.order || [];
                        let attr = subInclude.attributes || {};
                        wh[subInclude.targetKey] = {$in: fk};
                        let tObj = await subInclude.modal.findAll({where: wh, order: odr, attributes: attr});
                        tObj = helper.parse(tObj);

                        targetObjects.forEach(f => {
                            if (subInclude.relation === 'one') {
                                f[subInclude.as] = tObj.find(tf => tf[subInclude.targetKey] == f[subInclude.foreignKey]);
                            } else if (subInclude.relation === 'many') {
                                f[subInclude.as] = tObj.filter(tf => tf[subInclude.targetKey] == f[subInclude.foreignKey]);
                            } else if (subInclude.relation === 'has') {
                                f[subInclude.as] = tObj.length > 0;
                            }
                        })
                    }
                }

                // 抽取合并
                ret.rows.forEach(m => {
                    if (currentInclude.relation === 'one') {
                        m[currentInclude.as] = targetObjects.find(f => f[currentInclude.targetKey] == m[currentInclude.foreignKey]);
                    } else if (currentInclude.relation === 'many') {
                        m[currentInclude.as] = targetObjects.filter(f => f[currentInclude.targetKey] == m[currentInclude.foreignKey])
                    } else if (currentInclude.relation === 'has') {
                        m[currentInclude.as] = targetObjects.length > 0
                    }
                })
            }
        }
    }

    return pager.gridWrap(ret.rows, page, limit, ret.count);
};

/**
 *
 * @param modal     主模型
 * @param where     查询条件
 * @param order     排序
 * @param include   join操作
 *         [
 *             {foreignKey:主表外键，targetKey:'链接表主键'，modal: 链接表模型，where:查询条件，order：排序，attributes:属性，as:'别名'，relation:'one' or 'many' or 'has', include:[子子查询]}
 *         ]
 * @param attributes
 * @returns {Promise<*>}
 */
helper.getRow = async (modal, where = {}, order = [], include = [], attributes = {}) => {
    let ret = await modal.findOne({
        attributes: attributes,
        where: where,
        order: order,
    });

    ret = helper.parse(ret);

    // 模拟join 操作
    if (!_.isEmpty(include)) {
        if (ret && _.isObject(ret) && !_.isEmpty(ret)) {
            // 可能有多个join 字段
            for (let ii = 0; ii < include.length; ii++) {
                let currentInclude = include[ii];

                // 提取外键并去重
                let foreignKeys = ret[currentInclude.foreignKey];

                let where = currentInclude.where || {};
                let order = currentInclude.order || [];
                let attributes = currentInclude.attributes || {};

                where[currentInclude.targetKey] = {$in: [foreignKeys]};
                let targetObjects = await currentInclude.modal.findAll({
                    where: where,
                    order: order,
                    attributes: attributes
                });

                targetObjects = helper.parse(targetObjects);

                // 内部查询
                if (!_.isEmpty(currentInclude.include)) {
                    for (let cii = 0; cii < currentInclude.include.length; cii++) {
                        let subInclude = currentInclude.include[cii];

                        let fk = targetObjects.map(m => m[subInclude.foreignKey]).filter((f, i, a) => a.findIndex(fa => fa === f) === i);
                        let wh = subInclude.where || {};
                        let odr = subInclude.order || [];
                        let attr = subInclude.attributes || {};
                        wh[subInclude.targetKey] = {$in: fk};
                        let tObj = await subInclude.modal.findAll({where: wh, order: odr, attributes: attr});
                        tObj = helper.parse(tObj);

                        targetObjects.forEach(f => {
                            if (subInclude.relation === 'one') {
                                f[subInclude.as] = tObj.find(tf => tf[subInclude.targetKey] == f[subInclude.foreignKey]);
                            } else if (subInclude.relation === 'many') {
                                f[subInclude.as] = tObj.filter(tf => tf[subInclude.targetKey] == f[subInclude.foreignKey]);
                            } else if (subInclude.relation === 'has') {
                                f[subInclude.as] = tObj.length > 0;
                            }
                        })
                    }
                }

                // 抽取合并
                if (currentInclude.relation === 'one') {
                    ret[currentInclude.as] = targetObjects.find(f => f[currentInclude.targetKey] == ret[currentInclude.foreignKey]);
                } else if (currentInclude.relation === 'many') {
                    ret[currentInclude.as] = targetObjects.filter(f => f[currentInclude.targetKey] == ret[currentInclude.foreignKey])
                } else if (currentInclude.relation === 'has') {
                    ret[currentInclude.as] = targetObjects.length > 0
                }
            }
        }
    }

    return ret;
};
